/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *
 * http://www.gnu.org/copyleft/gpl.html
 */
package l1j.server.server.model;

import java.util.HashSet;
import java.util.logging.Logger;

import l1j.server.server.model.Instance.L1NpcInstance;
import l1j.server.server.model.Instance.L1PcInstance;
import l1j.server.server.model.Instance.L1PetInstance;
import l1j.server.server.model.Instance.L1SummonInstance;
import l1j.server.server.model.map.L1Map;
import l1j.server.server.model.map.L1WorldMap;
import l1j.server.server.model.skill.L1SkillId;
import l1j.server.server.serverpackets.S_CharVisualUpdate;
import l1j.server.server.serverpackets.S_DeleteNewObject;
import l1j.server.server.serverpackets.S_DeleteObjectFromScreen;
import l1j.server.server.serverpackets.S_MapID;
import l1j.server.server.serverpackets.S_OtherCharPacks;
import l1j.server.server.serverpackets.S_OwnCharPack;
import l1j.server.server.serverpackets.S_PetPack;
import l1j.server.server.serverpackets.S_SkillSound;
import l1j.server.server.serverpackets.S_SummonPack;

public class L1Teleport {
	private static Logger _log = Logger.getLogger(L1Teleport.class.getName());

	private L1Teleport() {

	}

	public static void teleport(L1PcInstance player, int x, int y, short map,
			int head, boolean effectable) {
		if (player.isDead()) {
			return;
		}
		if (!isInMap(x, y, map)) { // マップの範囲外なら現在地にテレポート
			x = player.getX();
			y = player.getY();
			map = player.getMapId();
		}

		player.setTeleport(true);
		if (effectable) {
			player.sendPackets(new S_SkillSound(player.getId(), 169));
			player.broadcastPacket(new S_SkillSound(player.getId(), 169));
		}
		// player.sendPackets(new S_OwnCharStatus(player));
		// player.removeAllKnownObjects();
		player.broadcastPacket(new S_DeleteObjectFromScreen(player));

		L1WorldMap.getInstance().getMap(player.getMapId()).setPassable(
				player.getX(), player.getY(), true);

		if (effectable) {
			// ディレイが無いと、テレポートのグラフィックが表示されないので
			// 最低限のディレイは入れておく
			try {
				// Thread.sleep(400);
				Thread.sleep(15);
			} catch (Exception e) {
			}
		}

		player.sendPackets(new S_DeleteNewObject(player));

		L1World.getInstance().moveVisibleObject(player, map);
		player.setX(x);
		player.setY(y);
		player.setMap(map);
		L1WorldMap.getInstance().getMap(player.getMapId()).setPassable(
				player.getX(), player.getY(), true);
		player.setHeading(head);
		player.sendPackets(new S_MapID(player.getMapId(), player.getMap()
				.isUnderwater()));

		// player.sendPackets(new S_OwnCharStatus(player));
		if (!player.isGhost() && !player.isGmInvis() && !player.isInvisble()) {
			player.broadcastPacket(new S_OtherCharPacks(player));
		}
		player.sendPackets(new S_OwnCharPack(player));
		player.sendPackets(new S_CharVisualUpdate(player));

		player.removeAllKnownObjects();
		player.sendVisualEffectAtTeleport(); // クラウン、毒、水中等の視覚効果を表示
		player.updateObject();

		player.killSkillEffectTimer(L1SkillId.MEDITATION);

		/*
		 * subjects ペットとサモンのテレポート先画面内へ居たプレイヤー。
		 * 各ペット毎にUpdateObjectを行う方がコード上ではスマートだが、
		 * ネットワーク負荷が大きくなる為、一旦Setへ格納して最後にまとめてUpdateObjectする。
		 */
		HashSet<L1PcInstance> subjects = new HashSet<L1PcInstance>();
		subjects.add(player);

		if (!player.isGhost() && player.getMap().isTakePets()) {
			// ペットとサモンも一緒に移動させる。
			for (L1NpcInstance petNpc : player.getPetList().values()) {

				// テレポート先の設定
				L1Location loc = player.getLocation().randomLocation(3, false);
				int nx = loc.getX();
				int ny = loc.getY();

				teleport(petNpc, nx, ny, map, head);
				if (petNpc instanceof L1SummonInstance) { // サモンモンスター
					L1SummonInstance summon = (L1SummonInstance) petNpc;
					player.sendPackets(new S_SummonPack(summon, player));
				} else if (petNpc instanceof L1PetInstance) { // ペット
					L1PetInstance pet = (L1PetInstance) petNpc;
					player.sendPackets(new S_PetPack(pet, player));
				}

				for (L1PcInstance pc : L1World.getInstance()
						.getRecognizePlayer(petNpc)) {
					// テレポート元と先に同じPCが居た場合、正しく更新されない為、一度removeする。
					pc.removeKnownObject(petNpc);
					subjects.add(pc);
				}

			}
		}

		for (L1PcInstance pc : subjects) {
			pc.updateObject();
		}

		player.setTeleport(false);
	}

	public static boolean isInMap(int x, int y, short map) {
		return L1WorldMap.getInstance().getMap(map).isInMap(x, y);
	}

	private static void teleport(L1NpcInstance npc, int x, int y, short map,
			int head) {
		L1World.getInstance().moveVisibleObject(npc, map);

		L1WorldMap.getInstance().getMap(npc.getMapId()).setPassable(npc.getX(),
				npc.getY(), true);
		npc.setX(x);
		npc.setY(y);
		npc.setMap(map);
		npc.setHeading(head);
		L1WorldMap.getInstance().getMap(npc.getMapId()).setPassable(npc.getX(),
				npc.getY(), false);
	}

	/*
	 * targetキャラクターのdistanceで指定したマス分前にテレポートする。指定されたマスがマップでない場合何もしない。
	 */
	public static void teleportToTargetFront(L1Character cha,
			L1Character target, int distance) {
		int locX = target.getX();
		int locY = target.getY();
		int heading = target.getHeading();
		L1Map map = target.getMap();
		short mapId = target.getMapId();

		// ターゲットの向きからテレポート先の座標を決める。
		switch (heading) {
		case 1:
			locX += distance;
			locY -= distance;
			break;

		case 2:
			locX += distance;
			break;

		case 3:
			locX += distance;
			locY += distance;
			break;

		case 4:
			locY += distance;
			break;

		case 5:
			locX -= distance;
			locY += distance;
			break;

		case 6:
			locX -= distance;
			break;

		case 7:
			locX -= distance;
			locY -= distance;
			break;

		case 0:
			locY -= distance;
			break;

		default:
			break;

		}

		if (map.isPassable(locX, locY)) {
			if (cha instanceof L1PcInstance) {
				teleport((L1PcInstance) cha, locX, locY, mapId, cha
						.getHeading(), true);
			} else if (cha instanceof L1NpcInstance) {
				((L1NpcInstance) cha).teleport(locX, locY, cha.getHeading());
			}
		}
	}

	public static void randomTeleport(L1PcInstance pc, boolean effectable) {
		// まだ本サーバのランテレ処理と違うところが結構あるような・・・
		L1Location newLocation = pc.getLocation().randomLocation(200, true);
		int newX = newLocation.getX();
		int newY = newLocation.getY();
		short mapId = (short) newLocation.getMapId();

		L1Teleport.teleport(pc, newX, newY, mapId, 5, effectable);
	}
}
